/**
 * 
 * @param {String} color
 * @param {Number} width
 */
var Pen = function(color, width, opacity) {
	this.color = color;
	this.width = width;
	this.opacity = opacity;
}

/**
 * 
 * @param {Number} x
 * @param {Number} y
 * @param {Pen} pen
 * @param {Number} timestamp
 */
var Step = function(x, y, pen, timestamp) {
	this.x = x;
	this.y = y;
	this.pen = pen;
	this.timestamp = timestamp;
};

var Path = function() {
	this.steps = [];
};

/**
 * 
 * @param {Step} step
 */
Path.prototype.addStep = function(step) {
	var lastStep = this.steps[this.steps.length - 1];
	if (lastStep) {
		if (Math.abs((lastStep.x - step.x) * (lastStep.y - step.y)) < 9 && step.timestamp - lastStep.timestamp < 100) {
			return false;
		}
	}
	this.steps.push(step);
	return true;
};

Path.prototype.reBuild = function() {
	var length = this.steps.length;
	if (length < 2) return;
	var steps = [];
	var lastStep = this.steps[0];
	steps.push(lastStep);
	for (var i = 1; i < length; i ++) {
		var step = this.steps[i];
		if (Math.abs((lastStep.x - step.x) * (lastStep.y - step.y)) > 9 || step.timestamp - lastStep.timestamp > 300) {
			steps.push(step);
			lastStep = step;
		}
	}
	this.steps = steps;
};
/**
 * 
 * @param {HTMLCanvasElement} canvas
 */
var PImage = function(canvas) {
	this._canvas = canvas;
	this.init();
};

PImage.prototype.init = function() {
	this._paths = [];
	this._oldPaths = [];
	this._ctx = this._canvas.getContext('2d');
	this._ctx.lineCap = "round";
	this._ctx.lineJoin = "round";
	this.addEvent();
};

PImage.prototype.addEvent = function() {
	var mobile = !!/mobile/i.test(navigator.userAgent);
	var START_EVENT = mobile ? "touchstart" : "mousedown";
	var MOVE_EVENT = mobile ? "touchmove" : "mousemove";
	var END_EVENT =  mobile ? "touchend" : "mouseup";
	var that = this;
	var path, pen;

	var start = function(ev) {
		var point = ev.touches ? ev.touches[0] : ev;
		path = new Path();
		pen = settings.toPen();
		var step = new Step(point.pageX, point.pageY, pen, Date.now());
		path.addStep(step);
		that.addPath(path);
		that._canvas.addEventListener(MOVE_EVENT, move, false)
	};

	var move = function(ev) {
		var point = ev.touches ? ev.touches[0] : ev;
		var step = new Step(point.pageX, point.pageY, pen, Date.now());
		if (path.addStep(step)) {
			that.draw();
		};
	};

	var end = function(ev) {
		that._canvas.removeEventListener(MOVE_EVENT, move);
		var point = ev.touches ? ev.touches[0] : ev;
		var step = new Step(point.pageX, point.pageY, pen, Date.now());
		path.addStep(step);
		that.draw();
	};

	this._canvas.addEventListener(START_EVENT, start, false);
	this._canvas.addEventListener(END_EVENT, end, false);
};

/**
 * 
 * @param {Path} path
 */
PImage.prototype.addPath = function(path) {
	this._paths.push(path);
	this._oldPaths.length = 0;
};

PImage.prototype.undo = function() {
	var path = this._paths.pop();
	if (path) {
		this._oldPaths.push(path);
		this.reDraw();
	}
};

PImage.prototype.redo = function() {
	var path = this._oldPaths.pop();
	if (path) {
		this._paths.push(path);
		this.reDraw();
	}
};

/**
 * 
 * @param {Step} step1
 * @param {Step} step2
 */
PImage.prototype.draw = function(step1, step2) {
	if (!this._ctx) {
		return;
	}
	var ctx = this._ctx;
	if (!(step1 && step2)) {	
		if (this._paths.length === 0) {
			return;
		}
		var path = this._paths[this._paths.length - 1];
		if (path.steps.length < 2) {
			return 0;
		}
		
		var length = path.steps.length;
		step1 = path.steps[length - 2];
		step2 = path.steps[length - 1];
	}
	if (step2.pen.color) {
		ctx.strokeStyle = step2.pen.color;
		ctx.globalCompositeOperation = "source-over";
	} else {
		ctx.globalCompositeOperation = "destination-out";	
	}
	ctx.lineWidth = step2.pen.width;
	ctx.globalOpacity = step2.pen.opacity;
	ctx.beginPath();
	ctx.moveTo(step1.x, step1.y);
	ctx.lineTo(step2.x, step2.y);
	ctx.closePath();
	ctx.stroke();
};

PImage.prototype.reDraw = function() {
	var that = this;
	this._canvas.width = this._canvas.width;
	this._paths.forEach(function(path) {
		if (path.steps.length < 2) return;
		for (var i = 1, n = path.steps.length; i < n; i ++) {
				that.draw(path.steps[i - 1], path.steps[i]);
		}
	});
};

PImage.prototype.reBuildPath = function() {
	var length = this._paths.length;
	var path = this._paths[length - 1];
	if (path && path.steps.length > 1) {
		path.reBuild();
	}
	this.reDraw();
};

